#ifndef FORMAT_H
#define	FORMAT_H

#include <yaml-cpp/yaml.h>
#include <string>
#include "Utils.h"
#include "ValidityException.h"

#define syvArrayDefaultLengthIsMaxLen 0x00000001 // Number of items in sequence type's default value, when requested, is the same as that array's maxlen property, if set.
                                                 // Otherwise it is the same length as the default value, if set. If not set, it is 0 (empty sequence).
#define syvAllowNullDefaultTrue       0x00000002 // When set allowNull has true as default. When not set, allowNull has false as default.
#define syvCopyInput                  0x00000004 // Copy the DDL tree input, as opposed to refering to it.

class FormatValidator {
public:
    FormatValidator(YAML::Node format, unsigned int flags = syvArrayDefaultLengthIsMaxLen|syvAllowNullDefaultTrue|syvCopyInput);
    inline bool isValid() const { return valid; }
    
    typedef ValidityException DDLException;
    typedef ValidityException InternalErrorException;

private:
    friend class YamlValidator;
    YAML::Node types;   // Map with encountered typenames as keys and type definition nodes as values. Entries may be Null, which means the type was encountered but did not (yet) validate.
    YAML::Node format;  // The input, or a copy of it. See flag syvCopyInput.
    bool valid;         // If the input validated.
    unsigned int flags; // Copy of flags passed.

    /**
     * Helper class to determine which members a given struct definition actually contains, since there may be inheritance.
     * Creates a new struct member map, with references to base class members.
     */
    class StructMembers {
    public:
        // here, definition needs to be the actual definition of a struct, with "struct" key.
        void recurse(YAML::Node definition, std::string structname = "");
        // Fetches one big struct member map (without "struct" keyword) containing the declarations of all members, own and inherited, refered to by definition.
        // structdefinition need not be the definition of the struct, it may be an anonymous type definition with a reference to struct.
        static YAML::Node getStructMembers(FormatValidator*fv, YAML::Node structdefinition);
        static bool isValid(FormatValidator*fv, YAML::Node structdefinition);
    private:
        StructMembers(FormatValidator*fv) : val(fv), members(YAML::NodeType::Undefined), error(0) {}
        bool validate(YAML::Node definition);
        FormatValidator*val;
        YAML::Node members; // map of membername/memberdefinition pairs
        int error;
    };

    /**
     * Helper class to determine which values a given enum definition actually can be. (Resolve inheritance).
     */
    class EnumValues {
    public:
        void recurse(YAML::Node enumdefinition);
        static YAML::Node getEnumValues(FormatValidator*fv, YAML::Node enumdefinition);
        inline static bool isValid(FormatValidator*fv, YAML::Node enumdefinition) { return EnumValues(fv).validate(enumdefinition); }
    private:
        EnumValues(FormatValidator*fv) : val(fv), mapOrSequence(0), values(YAML::NodeType::Undefined), error(0) {}
        bool validate(YAML::Node enumdefinition);
        FormatValidator*val;
        int mapOrSequence; // 0: not defined yet, 1: sequence, 2: map
        YAML::Node values;
        int error;
    };

    // Functions that get type definition members, or infer them from the type hierarchy.

    double getEffectiveMin(YAML::Node def);             // The value of min effective for given type definition
    double getEffectiveMax(YAML::Node def);             // The value of max effective for given type definition
    unsigned getEffectiveMaxlen(YAML::Node def);        // The value of maxlen effective for given type definition
    std::string getEffectiveFormat(YAML::Node def);     // The value of format effective for given type definition
    bool getEffectiveAllowNull(YAML::Node def, std::string type = "");

    // Functions related to variable values of a certain type.

    bool satisfiesTypeRequirements(std::string type, YAML::Node value, YAML::Node owningTypeDefinition);
    YAML::Node getDefault(YAML::Node def, YAML::Node overrideDefault = YAML::Node(YAML::NodeType::Undefined), unsigned overrideMaxlen = 0);
    YAML::Node getDefaultStruct(YAML::Node def, YAML::Node dflt = YAML::Node(YAML::NodeType::Undefined));
    YAML::Node getDefaultArray(YAML::Node def, std::string type = "", unsigned overriddenMaxlen = 0, YAML::Node overrideDefault = YAML::Node(YAML::NodeType::Undefined));
    YAML::Node getDefaultEnum(YAML::Node def, YAML::Node overrideDefault);
    bool isValidNumberForType(std::string type, std::string value);
    bool isValidValueForEnum(YAML::Node enumdefinition, YAML::Node value);
    bool satisfiesTypeRequirements(std::string type, YAML::Node value);

    // Functions that get information of a type name

    YAML::Node getType(std::string type);           // Returns definition of type, or Undefined if builtin type or array or not known type.
    std::string arrayElementType(std::string t);    // Returns type of element of array type passed. May be array in its turn.
    std::string getBaseType(std::string type);      // Traverses type hierarchy until it cannot go further back, because builtin type or struct or enum or unknown type.
    bool isFloatingPointType(std::string t);
    bool isNumericType(std::string t);
    bool isIntegerType(std::string t);
    bool isBuiltinType(std::string t);
    bool isEnumType(std::string t);
    bool isType(std::string t);
    bool hasBooleanValue(YAML::Node n);
    bool isArrayType(std::string t);
    bool isTypeStruct(std::string type);
    bool isTypeDefinitionStruct(YAML::Node n);
    bool isTypeDefinitionEnum(YAML::Node n);
    bool isString(std::string);

    // Functions related to validation.
    
    bool validateFormat();
    bool validateTypeDefinitionEnum(std::string name, YAML::Node n);
    bool validateTypeDefinitionVarDefinition(YAML::Node n);
    bool validateTypeDefinitionStruct(YAML::Node n);
    bool validateTypeDefinition(std::string name, YAML::Node n);
    bool validateVariableLocations(YAML::Node n, std::string trace = "", int depth = 0) throw(DDLException);
    bool validateTypeDefinitions(YAML::Node n);
    bool verifyMaxlen(YAML::Node def);
    bool verifyMin(YAML::Node def);
    bool verifyMax(YAML::Node def);
    bool ascertainDefaultLengthNotTooLarge(YAML::Node def, unsigned overrideMaxlen = 0);
    bool checkRefList(std::string type, std::vector<std::string>&list);                                         // Sanity checks, includes cyclic reference check.
};

#endif	/* FORMAT_H */
